#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "src/lta.h"
#include "src/ltbase.h"
#include "src/lterr.h"
#include "src/ltlog.h"
#include "src/ltopt.h"

lt_UsingType(lt, USize);

typedef struct {
    bool help;
    lta_Strings files;
} Option;

static int
RelayFile(FILE* reader, FILE* writer) {
    static char buf[65536];

    while (!feof(reader)) {
        size_t n = fread(buf, 1, sizeof(buf), reader);

        if (n == 0) {
            if (ferror(reader))
                return 1;
            break;
        }

        if (fwrite(buf, 1, n, writer) != n) {
            ltlog_Log1(Error, "failed to write byte", "count:", U64(n));
            return 1;
        }
    }

    return 0;
}

static void
Usage(void) {
    ltopt_Usage usage = {0};

    usage.name = "cau";
    usage.help = "Concatenate FILE(s) to stdandard output";

    ltopt_ArgumentHelp arguments[] = {
        {.name = "FILE", .help = "File path to read from, - means standard"},
        {.name = NULL}};

    ltopt_OptionHelp options[] = {
        {.name = 'h', .help = "Print this help and exit"},
        {.name = 0},
    };

    usage.arguments = arguments;
    usage.options = options;

    ltopt_WriteUsage(&usage, stderr);
}

static lterr_Code
OnArgument(void* ctx, char name, char const* value) {
    Option* opt = (Option*)ctx;

    switch (name) {
        case 0: lta_Append(opt->files, value); break;
        case 'h': opt->help = true; break;
        default: return lterr_kError;
    }

    return lterr_kOk;
}

int
cat_Run(int argc, char const** argv) {
    Option opt = {0};

    lterr_Code r = ltopt_Parse(argc, argv, "", OnArgument, &opt);
    if (r != 0)
        return 1;

    if (opt.help) {
        Usage();
        return 0;
    }

    if (opt.files.len == 0) {
        RelayFile(stdin, stdout);
        return 0;
    }

    int status_all = 0;
    for (USize i = 0; i < opt.files.len; ++i) {
        FILE* fp = strcmp(opt.files.data[i], "-") == 0
                       ? stdin
                       : fopen(opt.files.data[i], "rb");

        if (fp == NULL) {
            ltlog_Log1(Error, "failed to read input file",
                       "file path:", String(opt.files.data[i]));
            return 1;
        }

        int status = RelayFile(fp, stdout);
        if (status != 0)
            status_all = status;

        fclose(fp);
    }

    return status_all;
}
